//
// Created by fang on 20-5-6.
//
#include <fcntl.h>
#include <event.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "server.h"
#include "segmentation.h"

//重置客户端,为下一个请求作准备
void reset_client(struct client* client)
{
    client->input_need_len = 0;
    client->input[0] = '\0';
}

/**
 * 执行完成的指令
 *
 * @param bev
 * @param arg
 */
static void callback_job(struct bufferevent *bev, void *arg)
{
    struct client *client = (struct client*)arg;
    result* r;
    seg_buf *buf;
    r = seg(client->input);
    buf = string_result(r);

    char reply_msg[4096] = "";
    sprintf(&reply_msg, "%d\r\n%s", buf->data_len, buf->data);
    bufferevent_write(bev, reply_msg, strlen(reply_msg));
    free_result(r);
    seg_buf_free(buf);
    reset_client(client);
}


static int setnonblock(int fd) {
    int flags;
    flags = fcntl(fd, F_GETFL);
    if(flags<0)
        return flags;
    flags |= O_NONBLOCK;
    if(fcntl(fd, F_SETFL, flags) < 0)
        return -1;
    return 0;
}

/**
 * 释放 bufevent
 *
 * @param bev
 * @param arg
 */
static void distruct_buffered(struct bufferevent *bev, void *arg)
{
    struct client *client = (struct client*)arg;
    bufferevent_free(client->buf_ev);
    close(client->fd);
    free(client);
}

/**
 * 消息读取
 *
 * @param bev
 * @param arg
 */
static void buffered_on_read(struct bufferevent *bev, void *arg)
{
    struct client *client = (struct client*)arg;
    char msg[READ_LEN], *stop_str;
    unsigned int read_len = READ_LEN;

    do {
        if(client->input_need_len > 0) {
            read_len = client->input_need_len - strlen(client->input);
            size_t len = bufferevent_read(bev, msg, read_len>(READ_LEN-1) ? READ_LEN : read_len);
            msg[len] = '\0';
            strcat(client->input, msg);
            if(client->input_need_len < strlen(client->input)) continue;
            if(client->input_need_len >= strlen(client->input)) break;
            return;
        } else {
            //第一次读取 10个字节   预定9位数(约100m长度)
            size_t len = bufferevent_read(bev, msg, READ_LEN - 1);
            msg[len] = '\0';
            client->input_need_len = strtol(msg, &stop_str, 10);
            //检查格式是否正确
            if(client->input_need_len <= 0) {
                //非正确格式, 资源释放
                distruct_buffered(bev, arg);
                return;
            }
            strcat(client->input, stop_str);
            //检查读取到的字符串长度是否超出长度
            if(strlen(client->input) >= client->input_need_len) {
                break;
            }
        }
    } while (1);

    //检查是否读完
    if(strlen(client->input) != client->input_need_len) {
        return;
    }

    //执行业务逻辑
    callback_job(bev, arg);
}

/**
 * 消息写入(低水位回调)
 *
 * @param bev
 * @param arg
 */
static void buffered_on_write(struct bufferevent *bev, void *arg)
{

}

/**
 * 错误处理
 *
 * @param bev
 * @param what
 * @param arg
 */
static void buffered_on_error(struct bufferevent *bev, short what, void *arg)
{
    if(what & EVBUFFER_EOF) {
        printf("client disconnected.\n");
    } else {
        printf("client socket error, disconnecting.\n");
    }
    distruct_buffered(bev, arg);
}



/**
 * 监听客户端连接
 *
 * @param fd
 */
static void on_accept(int fd, short ev, void *arg) {
    int client_fd;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    struct client *client;

    client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
    if(client_fd<0) {
        //TODO warning log
        return;
    }
    if(setnonblock(client_fd)<0) {
        printf("error: setnoblock error");
        fclose(client_fd);
        return;
    }
    client = calloc(1, sizeof(*client));
    if(client == NULL) {
        printf("error: call calloc");
        abort();
    }
    client->fd = client_fd;
    client->input[0] = '\0';
    client->input_need_len = 0;
    client->buf_ev = bufferevent_new(client_fd, buffered_on_read, buffered_on_write, buffered_on_error, client);
    client->buf_ev->wm_read.high = 100;
    client->buf_ev->wm_read.low = 0;
    client->buf_ev->wm_write.high = 2;
    client->buf_ev->wm_write.low = 0;

    bufferevent_enable(client->buf_ev,EV_READ|EV_WRITE);
    printf("Accepted connection from %s\n", inet_ntoa(client_addr.sin_addr));
}

void start_server() {
    int listen_fd;
    struct sockaddr_in listen_addr;
    struct event ev_accept;
    int reuseaddr_on = 1;

    event_init();
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_fd<0) {
        printf("error: can't create socket");
        abort();
    }
    memset(&listen_addr, 0, sizeof(listen_addr));
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_addr.s_addr = INADDR_ANY;
    //TODO defined port
    listen_addr.sin_port = htons(9999);
    if(bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0) {
        printf("error: bind failed");
        abort();
    }

    int yes = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

    if(listen(listen_fd, 100) < 0) {
        printf("error: listen error");
        abort();
    }
    //socket 地址复用
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on));
    if(setnonblock(listen_fd)<0) {
        printf("error: setnonblock failed");
        abort();
    }
    event_set(&ev_accept, listen_fd, EV_READ | EV_PERSIST, on_accept, NULL);
    event_add(&ev_accept, NULL);
    event_dispatch();
}

